package br.ufrj.dcc.compconc.vista;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.OutputStream;
import java.io.PrintStream;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import br.ufrj.dcc.compconc.controlador.Controlador;

/**
 * MainWindow surge como a interface grafica da aplicacao. Contem campos para as entradas e uma area para o log se saida
 * @author Joao Paulo de Freitas Ramirez e Joao Lucas Nery
 *
 */
public class MainWindow {
	private static MainWindow instancia;
	
	private Controlador controlador;
	
	private JFrame janelaPrincipal;
	
	private JPanel painelPrincipal;
	private JPanel painelEntrada;
	private JPanel painelProgJava;
	private JPanel painelProgC;
	private JPanel painelLog;
	private JPanel painelBotao;
	
	private JLabel labelProgC;
	private JLabel labelProgJava;
	private JLabel labelLog;
	
	private JTextField textFieldNumeroProgC;
	private JTextField textFieldNumeroProgJava;
	private JTextArea textAreaLog;
	
	private JScrollPane textAreaScrollPane;
	
	private JButton botaoIniciar;
	private JButton botaoParar;
	
	/**
	 * O construtor invoca os metodos a partir dos quais os componentes da interface grafica sao montados
	 * e instancia o controlador para chamar a execucao do programa
	 * @see Controlador#getInstance()
	 */
	private MainWindow(){
		this.montaJanelaPrincipal();
		this.montaPainelPrincipal();
		this.montaPainelEntrada();
		this.montaPainelProgJava();
		this.montaPainelProgC();
		this.montaPainelLog();
		this.montaPainelBotao();
		
		this.controlador = Controlador.getInstance();
	}
	
	/**
	 * Implementacao do singleton para a janela principal
	 * @return uma instancia de <code>MainWindow</code>
	 */
	public static MainWindow getInstance(){
		if(instancia == null)MainWindow.instancia = new MainWindow();
		return MainWindow.instancia;
	}
	
	/**
	 * Monta a janela principal, um JFrame e ajusta suas caracteristicas
	 */
	private void montaJanelaPrincipal(){
		this.janelaPrincipal = new JFrame("Trabalho 2 Computacao Concorrente");
		this.janelaPrincipal.pack();
		this.janelaPrincipal.setSize(540, 540);
		this.janelaPrincipal.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.janelaPrincipal.setVisible(true);

	}
	
	/**
	 * Monta a painel principal, um JPanel e adiciona-o ao seu componente pai, janelaPrincipal
	 * @see MainWindow#montaJanelaPrincipal()
	 */
	private void montaPainelPrincipal(){
		this.painelPrincipal = new JPanel(new BorderLayout());
		this.janelaPrincipal.add(this.painelPrincipal);
	}
	
	/**
	 * Monta o painel que contem as componentes que servem de entrada de dados
	 * do programa, um JPanel, ajusta seu layout e o adiciona ao seu componente pai, painelPrincipal
	 * @see MainWindow#montaPainelPrincipal()
	 */
	private void montaPainelEntrada(){
		this.painelEntrada = new JPanel();
		this.painelEntrada.setLayout(new GridLayout());
		this.painelPrincipal.add(this.painelEntrada, BorderLayout.NORTH);
	}
	
	/**
	 * Monta o painel que possui o campo de entrada que diz respeito ao numero de 
	 * programadores Java que se deseja que facam a travessia. Adiciona esse painel ao seu
	 * componente pai, painelEntrada
	 * @see MainWindow#montaPainelEntrada()
	 */
	private void montaPainelProgJava(){
		this.painelProgJava = new JPanel();
		this.painelProgJava.setLayout(new BorderLayout());
		
		this.labelProgJava = new JLabel("Numero de Programadores Java:");
		
		this.textFieldNumeroProgJava = new JTextField(8);
		this.textFieldNumeroProgJava.setEditable(true);
		
		this.painelProgJava.add(this.labelProgJava, BorderLayout.NORTH);
		this.painelProgJava.add(this.textFieldNumeroProgJava, BorderLayout.SOUTH);
		
		this.painelEntrada.add(this.painelProgJava);

	}
	
	/**
	 * Monta o painel que possui o campo de entrada que diz respeito ao numero de 
	 * programadores C que se deseja que facam a travessia. Adiciona esse painel ao seu
	 * componente pai, painelEntrada
	 * @see MainWindow#montaPainelEntrada()
	 */
	private void montaPainelProgC(){
		this.painelProgC = new JPanel();
		this.painelProgC.setLayout(new BorderLayout());
		
		this.labelProgC = new JLabel("Numero de Programadores C:");
		
		this.textFieldNumeroProgC = new JTextField(8);
		this.textFieldNumeroProgC.setEditable(true);
		
		this.painelProgC.add(this.labelProgC, BorderLayout.NORTH);
		this.painelProgC.add(this.textFieldNumeroProgC, BorderLayout.SOUTH);
		
		this.painelEntrada.add(this.painelProgC);
	}
	
	/**
	 * Monta o painel que contera o log da saida do programa executado. A saida padrao do programa eh redirecionada para
	 * a JTextArea contida nesse painel
	 * @see MainWindow#redirecionaSaidaPadrao(JTextArea)
	 */
	private void montaPainelLog(){
		this.painelLog = new JPanel();
		this.painelLog.setLayout(new BoxLayout(this.painelLog, BoxLayout.Y_AXIS));
		
		this.labelLog = new JLabel("Log de Saida");
		
		this.textAreaLog = new JTextArea();
		this.textAreaLog.setEditable(false);
		this.redirecionaSaidaPadrao(this.textAreaLog);
		
		this.textAreaScrollPane = new JScrollPane(this.textAreaLog, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		
		this.painelLog.add(this.labelLog);
		this.painelLog.add(this.textAreaScrollPane);
		
		this.painelPrincipal.add(this.painelLog, BorderLayout.CENTER);
	}
	
	/**
	 * Monta o painel onde se localiza o botao que gera o evento para iniciar a execucao do programa
	 * @see Controlador#executa(int, int)
	 */
	private void montaPainelBotao(){
		this.painelBotao = new JPanel();
		
		this.botaoIniciar = new JButton("Iniciar");
		this.botaoParar = new JButton("Parar");
		
		this.botaoIniciar.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e){
				controlador.executa(Integer.parseInt(textFieldNumeroProgC.getText()), Integer.parseInt(textFieldNumeroProgJava.getText()));
				textAreaLog.setText("");
			}
		});
		
		
		this.painelBotao.add(this.botaoIniciar);
		this.painelBotao.add(this.botaoParar);
		
		this.painelPrincipal.add(this.painelBotao, BorderLayout.SOUTH);
	}
	
	/**
	 * Redireciona a saida padrao do console para a JTextArea especificada.
	 * Cria uma classe anonima que implementa OutputStream e redefine os metodos write()
	 * @see OutputStream#write(byte[])
	 * @see OutputStream#write(int)
	 * @see System#setOut(PrintStream)
	 * @param textComponent a JTextArea que sera usado para a saida
	 */
	private void redirecionaSaidaPadrao(final JTextArea textComponent){
		OutputStream out = new OutputStream(){  
            public void write(byte[] b){  
                for(int i=0; i<b.length; i++){  
                    write(b[i]);  
                }  
            }  
              
            public void write(int i){  
                textComponent.append(Character.toString((char) i));  
            }  
        };  
          
        System.setOut(new PrintStream(out));
		
	}
	
}
